package cn.lzx.log;

import cn.lzx.log.annotation.ExcludeLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * Created by lizhenxing on 2017/8/2.
 */
// proxyTargetClass：默认为false，使用jdk生成代理类，true表示使用cglib。
// 但是如果目标对象没有声明接口，spring自动使用cglib生成代理。
// exposeProxy：是否将代理对象暴露给AOP上下文
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@Configuration
@Slf4j
@Aspect
public class LogInterceptor {

    @AfterReturning(pointcut = "@annotation(cn.lzx.log.annotation.IncludeLog)", returning = "reVal")
    public void afterLogByAnnotation(final JoinPoint joinPoint, final Object reVal) {
        try {
            printLog(joinPoint, reVal);
        } catch (Exception e) {
            log.error("intercept log encounter error.", e);
        }
    }

    @AfterThrowing(value = "@annotation(cn.lzx.log.annotation.IncludeLog)")
    public void exceptionLogByAnnotation(final JoinPoint joinPoint) {
        try {
            printLog(joinPoint);
        } catch (Exception e) {
            log.error("intercept log encounter error.", e);
        }
    }

    @AfterThrowing(value = "@within(cn.lzx.log.annotation.OpLog)")
    public void exceptionLogByWithin(final JoinPoint joinPoint) {
        try {
            printLog(joinPoint);
        } catch (Exception e) {
            log.error("intercept log encounter error.", e);
        }
    }

    @AfterReturning(pointcut = "@within(cn.lzx.log.annotation.OpLog)", returning = "reVal")
    public void afterLogByWithin(final JoinPoint joinPoint, final Object reVal) {
        try {
            Class<?> targetClass = joinPoint.getTarget().getClass();

            String methodName = joinPoint.getSignature().getName();
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Class<?>[] parameterTypes = methodSignature.getMethod().getParameterTypes();
            Method method = targetClass.getDeclaredMethod(methodName, parameterTypes);
            if (method.isAnnotationPresent(ExcludeLog.class)) {
                return;
            }

            printLog(joinPoint, reVal);

        } catch (Exception e) {
            log.error("intercept log encounter error.", e);
        }
    }

    private void printLog(final JoinPoint joinPoint, Object... reVal) {
        L l = L.createLog();
        l.setClazzName(joinPoint.getTarget().getClass().getName());
        l.setMethodName(joinPoint.getSignature().getName());
        l.setRequest(Arrays.toString(joinPoint.getArgs()));
        l.setBeginTime(new Date().toString());
        if (reVal != null) {
            l.setResponse(reVal.toString());
        }

        log.info(l.toMap().toString());
    }
}
